﻿
namespace Microsoft.Sdc.Tasks.Mithran
{
    using System.Globalization;
    using System;
    using System.Xml;
    using Microsoft.Build.Framework;
    using System.IO;
    using Microsoft.Sdc.Tasks.Configuration;

    public class CreateProject : TaskBase
    {
        private string productPath;
        private string projectName;
        private string rootNamespace;
        private string version;
        private string projectType;
        private string assemblyName;
        private string solutionName;
        private string[] projectReferences;
        private string templatePath;
        private int port;

        /// <summary>
        /// Initializes a new instance of the CreateProject class.
        /// </summary>
        public CreateProject()
        {
        }

        /// <summary>
        /// Only used for Webs
        /// </summary>
        /// <value></value>
        public int Port
        {
            get { return this.port; }
            set { this.port = value; }
        }

        public string[] ProjectReferences
        {
            get { return this.projectReferences; }
            set { this.projectReferences = value; }
        }

        /// <summary>
        /// Will default based on rootnamespace if not provided (with an approppriate ext)
        /// </summary>
        /// <value></value>
        public string AssemblyName
        {
            get
            {
                if (this.assemblyName == null)
                {
                    this.assemblyName = this.RootNamespace;
                }

                return this.assemblyName;
            }
            set { this.assemblyName = value; }
        }

        [Required]
        public string ProjectType
        {
            get { return (this.projectType == null ? String.Empty : this.projectType); }
            set { this.projectType = value; }
        }       

        [Required]
        public string ProductPath
        {
            get { return (this.productPath == null ? String.Empty : this.productPath); }
            set { this.productPath = value; }
        }

        [Required]
        public string ProjectName
        {
            get { return (this.projectName == null ? String.Empty : this.projectName); }
            set { this.projectName = value; }
        }

        [Required]
        public string RootNamespace
        {
            get { return (this.rootNamespace == null ? String.Empty : this.rootNamespace); }
            set { this.rootNamespace = value; }
        }

        [Required]
        public string SolutionName
        {
            get { return (this.solutionName == null ? String.Empty : this.solutionName); }
            set { this.solutionName = value; }
        }

        [Required]
        public string Version
        {
            get { return (this.version == null ? String.Empty : this.version); }
            set { this.version = value; }
        }

        /// <summary>
        /// Defaults to the folder we are running from with a Templates suffix
        /// </summary>
        /// <value></value>
        public string TemplatePath
        {
            get
            {
                if (this.templatePath == null)
                {
                    //Get our executing path and use that if not provided by the user

                    Uri uri = new Uri(this.GetType().Assembly.CodeBase);
                    this.templatePath = Path.Combine(Path.GetDirectoryName(uri.LocalPath), "Templates");
                }

                return this.templatePath;

            }
            set { this.templatePath = value; }
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            // create a new project of the correct type and version for the solution specified

            string newValue = null;
            #region Copy the template proj in
            string destinationFolder = Path.Combine(Path.Combine(Path.Combine(this.ProductPath, @"Main\Src\Solutions"), this.SolutionName), this.ProjectName);
            if (Directory.Exists(destinationFolder))
            {
                throw new TaskException("Solution.Exists");
            }

            Folder.CopyFolder copy = new Folder.CopyFolder();
            copy.BuildEngine = this.BuildEngine;

            string sourceFolder = Path.Combine(Path.Combine(Path.Combine(this.TemplatePath, "BlankProject"), this.Version), this.ProjectType);

            copy.Source = sourceFolder;
            copy.Destination = destinationFolder;

            copy.Execute();
            #endregion
            #region rename the proj file ( or dbp file) and .user file (if it exists)

            string projFilename;
            string userFilename = "";
            bool userFileExists = false;

            switch (this.projectType.ToLowerInvariant())
            {
                case "classlibrary":
                case "web":
                case "webservice":
                    {
                        projFilename = Path.Combine(destinationFolder, this.ProjectName + ".csproj");
                        System.IO.File.Move(Path.Combine(destinationFolder, "Template.csproj"), projFilename);

                        if (System.IO.File.Exists(Path.Combine(destinationFolder, "Template.csproj.user")))
                        {
                            userFileExists = true;
                            userFilename = Path.Combine(destinationFolder, this.ProjectName + ".csproj.user");
                            System.IO.File.Move(Path.Combine(destinationFolder, "Template.csproj.user"), userFilename);
                        }

                        break;
                    }
                case "database":
                    {
                        projFilename = Path.Combine(destinationFolder, this.ProjectName + ".dbp");
                        System.IO.File.Move(Path.Combine(destinationFolder, "Template.dbp"), projFilename);
                        break;
                    }

                default:
                    throw new TaskException("Unknown Project Type");
            }
            #endregion

            #region update the contents of the proj file for the new proj name

            Xml.ModifyFile modifyFile = new Xml.ModifyFile();
            modifyFile.BuildEngine = this.BuildEngine;

            switch (this.projectType.ToLowerInvariant())
            {
                case "classlibrary":
                case "web":
                case "webservice":
                    {
                        if (userFileExists)
                        {
                            modifyFile.Path = userFilename;
                            modifyFile.RegularExpression = "%Microsoft.Sdc.Template.Port%";
                            modifyFile.NewValue = this.Port.ToString();
                            modifyFile.Execute();                      
                        }

                        modifyFile.Path = projFilename;
                        modifyFile.RegularExpression = "%Microsoft.Sdc.Template.RootNamespace%";
                        modifyFile.NewValue = this.RootNamespace;
                        modifyFile.Execute();

                        modifyFile.Path = projFilename;
                        modifyFile.RegularExpression = "%Microsoft.Sdc.Template.AssemblyName%";
                        modifyFile.NewValue = this.AssemblyName;
                        modifyFile.Execute();

                        modifyFile.Path = projFilename;
                        modifyFile.RegularExpression = "%ProjectGuid%";
                        modifyFile.NewValue = Guid.NewGuid().ToString("B").ToUpperInvariant();
                        modifyFile.Execute();

                        if (this.projectReferences != null)
                        {
                            foreach (string projectReference in this.ProjectReferences)
                            {
                                string referencedProjectGuid = this.GetProjectGuid(projectReference);
                                string packageGuid = Guid.NewGuid().ToString("B").ToUpperInvariant();
                                newValue = "<Reference Name = \"" + projectReference + "\" Project = \"" + referencedProjectGuid + "\" Package = \"" + packageGuid + "\" /></References>";
                                modifyFile.Path = projFilename;
                                modifyFile.RegularExpression = "</References>";
                                modifyFile.NewValue = newValue;
                                modifyFile.Execute();
                            }
                        }
                        break;
                    }
                case "database":
                    {
                        modifyFile.Path = projFilename;
                        modifyFile.RegularExpression = "%Microsoft.Sdc.Template.ProjectName%";
                        modifyFile.NewValue = this.ProjectName;
                        modifyFile.Execute();
                        break;
                    }

                default:
                    throw new TaskException("Unknown Project Type");
            }

            #endregion

            #region now add it to the .sln file
            //For webs the projectShortPath needs to be a URL like http://localhost:82/Microsoft.Education.Lms.UI.Portal.csproj

            string solutionFilename = Path.Combine(Path.Combine(Path.Combine(this.ProductPath, @"Main\Src\Solutions"), this.SolutionName), this.SolutionName + ".sln");
            string projectGuid;
            string projectShortPath;
            string projectTypeGuid;
            bool addToSolutionBuildConfigs;

            switch (this.ProjectType.ToLowerInvariant())
            {
//                   {
//                        projectGuid = this.GetProjectGuid(this.ProjectName);
//                        projectShortPath = "http://localhost:" + this.Port + "/" + this.ProjectName + ".csproj";
//                        projectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
//                        addToSolutionBuildConfigs = true;
//                        break;
//                    }
                case "web":
                case "webservice":                 
                case "classlibrary":
                    {
                        projectGuid = this.GetProjectGuid(this.ProjectName);
                        projectShortPath = Path.Combine(this.ProjectName, this.ProjectName + ".csproj");
                        projectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
                        addToSolutionBuildConfigs = true;
                        break;
                    }
                case "database":
                    {
                        //just gen a new guid for DB projects
                        projectGuid = Guid.NewGuid().ToString("B").ToUpperInvariant();
                        projectShortPath = Path.Combine(this.ProjectName, this.ProjectName + ".dbp");
                        projectTypeGuid = "{4F174C21-8C12-11D0-8340-0000F80270F8}";
                        addToSolutionBuildConfigs = false;
                        break;
                    }
                default:
                    throw new TaskException("unknown project type");
            }

            newValue = "Microsoft Visual Studio Solution File, Format Version 8.00\r\nProject(\"" + projectTypeGuid +"\") = \"" + this.ProjectName + "\", \"" + projectShortPath + "\", \"" + projectGuid + "\"\r\n    ProjectSection(ProjectDependencies) = postProject\r\n   EndProjectSection\r\nEndProject";

            modifyFile.Path = solutionFilename;
            modifyFile.RegularExpression = "Microsoft Visual Studio Solution File, Format Version 8.00";
            modifyFile.NewValue = newValue;
            modifyFile.Execute();

            if (addToSolutionBuildConfigs)
            {
                newValue = "GlobalSection(ProjectConfiguration) = postSolution\r\n" + projectGuid + ".Debug.ActiveCfg = Debug|.NET\r\n\t\t" + projectGuid + ".Debug.Build.0 = Debug|.NET\r\n\t\t" + projectGuid + ".Release.ActiveCfg = Release|.NET\r\n\t\t" + projectGuid + ".Release.Build.0 = Release|.NET";
                modifyFile.Path = solutionFilename;
                modifyFile.RegularExpression = "GlobalSection\\(ProjectConfiguration\\) = postSolution";
                modifyFile.NewValue = newValue;
                modifyFile.Execute();
            }
            #endregion
        }

        private string GetProjectGuid(string projectName)
        {
            string projFile = Path.Combine(Path.Combine(Path.Combine(Path.Combine(this.ProductPath, @"Main\Src\Solutions"), this.SolutionName), projectName), projectName + ".csproj");


            XmlDocument projectDocument = new XmlDocument();

            projectDocument.Load(projFile);

            XmlElement languageElement = (XmlElement)projectDocument.ChildNodes[0].ChildNodes[0];

            return languageElement.GetAttribute("ProjectGuid").ToString().ToUpperInvariant();
        }
    }
}